//go:build !windows
// +build !windows

package shell

import (
	"errors"
	"io"
	"os"
	"os/exec"

	"github.com/creack/pty"
)

type Shell struct {
	exec   string
	env    []string
	stdin  io.WriteCloser
	stdout io.ReadCloser
	pid    int
}

func NewShell(exec string, env []string) *Shell {
	return &Shell{
		exec: exec,
		env:  env,
	}
}

// Exec execute shell command
func (s *Shell) Exec() error {
	var cmd *exec.Cmd
	if len(s.exec) > 0 {
		cmd = exec.Command(s.exec)
	}
	if cmd == nil {
		dir, err := exec.LookPath("bash")
		if err == nil {
			cmd = exec.Command(dir)
		}
	}
	if cmd == nil {
		dir, err := exec.LookPath("sh")
		if err == nil {
			cmd = exec.Command(dir)
		}
	}
	if cmd == nil {
		return errors.New("no shell command supported")
	}
	cmd.Env = append(os.Environ(), s.env...)
	f, err := pty.Start(cmd)
	if err != nil {
		return err
	}
	go cmd.Wait() // defunct process
	s.stdin = f
	s.stdout = f
	s.pid = cmd.Process.Pid
	return nil
}

func (s *Shell) Close() {
	if s.stdin != nil {
		s.stdin.Close()
	}
	if s.stdout != nil {
		s.stdout.Close()
	}
	if s.pid > 0 {
		// 杀掉进程
		proc, err := os.FindProcess(s.pid)
		if err == nil {
			proc.Kill()
		}
	}
}

func (s *Shell) Resize(rows, cols uint32) {
	if s.stdin != nil {
		pty.Setsize(s.stdin.(*os.File), &pty.Winsize{
			Rows: uint16(rows),
			Cols: uint16(cols),
		})
	}
}

func (s *Shell) Write(data []byte) (int, error) {
	if s.stdin != nil {
		return s.stdin.Write(data)
	}
	// 其他情况，直接返回0
	return 0, nil
}
func (s *Shell) Read(data []byte) (int, error) {
	if s.stdout != nil {
		return s.stdout.Read(data)
	}
	// 其他情况，直接返回0
	return 0, nil
}
func (s *Shell) Pid() int {
	return s.pid
}
